﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Shapes.Geometry;



namespace Shapes.Misc
{

#if MONO 
using idxer = System.UInt16;
#elif REACH
using idxer = System.Int16;
#else
using idxer = System.Int32; 
#endif

    public class PrimitiveDrawer
    {
        GraphicsDevice _Device;
        BasicEffect _Effect;

        public Matrix ViewMatrix { get; set; }

        public PrimitiveDrawer(GraphicsDevice graphicsDevice)
        {
            _Device = graphicsDevice;

            // init effect
            _Effect = new BasicEffect(_Device);
            _Effect.VertexColorEnabled = true;

            _Effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
            ResetProjectionMatrix(null);

            ViewMatrix = Matrix.Identity;

        }

        public void DrawLine(Line line, Color color)
        {
            DrawLine(line, color, false);
        }
        private void DrawLine(Line line, Color color, bool drawInLocalSpace)
        {
            VertexPositionColor[] points = new VertexPositionColor[2];
            idxer[] indices = new idxer[2];

            bool isViewTransformed = ViewMatrix != Matrix.Identity;

            Vector2 start = line._StartPoint;
            Vector2 end = line._EndPoint;

            if (!drawInLocalSpace)
            {
                line.Transform.TransformLocalToGlobal(ref start);
                line.Transform.TransformLocalToGlobal(ref end);
            }
            if (isViewTransformed)
            {
                start = Vector2.Transform(start, ViewMatrix);
                end = Vector2.Transform(end, ViewMatrix);
            }

            points[0] = new VertexPositionColor(start.ToVector3(Axis3.Z), color);
            points[1] = new VertexPositionColor(end.ToVector3(Axis3.Z), color); 

            indices[0] = 0;
            indices[1] = 1;

            ResetProjectionMatrix((drawInLocalSpace) ? line.Transform : null);

            _Effect.CurrentTechnique.Passes[0].Apply();

            _Device.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineList, points, 0, 2, indices, 0, 1);

        }

        public void DrawLinedGeometry(ILinedGeometry linedGeometry, Color color)
        {
            DrawLinedGeometry(linedGeometry, color, false);
        }
        private void DrawLinedGeometry(ILinedGeometry linedGeometry, Color color, bool drawInLocalSpace)
        {
            VertexPositionColor[] points = new VertexPositionColor[2 * linedGeometry.LineCount];
            idxer[] indices = new idxer[2 * linedGeometry.LineCount];

            bool isViewTransformed = ViewMatrix != Matrix.Identity;

            int idx = 0;
            foreach (Line line in linedGeometry.GetLines())
            {
                Vector2 start = line.StartPoint;
                Vector2 end = line.EndPoint;

                if (!drawInLocalSpace)
                {
                    linedGeometry.Transform.TransformLocalToGlobal(ref start);
                    linedGeometry.Transform.TransformLocalToGlobal(ref end);
                }
                if (isViewTransformed)
                {
                    start = Vector2.Transform(start, ViewMatrix);
                    end = Vector2.Transform(end, ViewMatrix);
                }

                points[idx] = new VertexPositionColor(start.ToVector3(Axis3.Z), color);
                points[idx + 1] = new VertexPositionColor(end.ToVector3(Axis3.Z), color);

                indices[idx] = (idxer)idx++;
                indices[idx] = (idxer)idx++;
            }

            ResetProjectionMatrix((drawInLocalSpace) ? linedGeometry.Transform : null);
            _Effect.CurrentTechnique.Passes[0].Apply();

            _Device.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineList, points, 0, 2 * linedGeometry.LineCount, indices, 0, linedGeometry.LineCount);

        }
        public void DrawLineStrip(LineStrip lineStrip, Color color)
        {
            DrawLineStrip(lineStrip, color, false);
        }
        private void DrawLineStrip(LineStrip lineStrip, Color color, bool drawInLocalSpace)
        {
            int count = (lineStrip.IsClosed) 
                ? lineStrip._Points.Count + 1
                : lineStrip._Points.Count;

            bool isViewTransformed = ViewMatrix != Matrix.Identity;

            VertexPositionColor[] points = new VertexPositionColor[count];
            idxer[] indices = new idxer[count];

            for (int i = 0; i < lineStrip._Points.Count; i++)
            {
                Vector2 pos = lineStrip._Points[i];
                if (!drawInLocalSpace)
                    lineStrip.Transform.TransformLocalToGlobal(ref pos);

                if (isViewTransformed)
                {
                    pos = Vector2.Transform(pos, ViewMatrix);
                }

                points[i] = new VertexPositionColor(pos.ToVector3(Axis3.Z), color);
                indices[i] = (idxer)i;
            }

            if (lineStrip.IsClosed)
            {
                points[count - 1] = points[0];
                indices[count - 1] = 0;
            }

            ResetProjectionMatrix((drawInLocalSpace) ? lineStrip.Transform : null);

            _Effect.CurrentTechnique.Passes[0].Apply();

            _Device.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, points, 0, count, indices, 0, count - 1);
        }

        private void ResetProjectionMatrix(Transformation2D transform)
        {
            float width  = (transform == null) ? _Device.Viewport.Width  : transform.Width;
            float height = (transform == null) ? _Device.Viewport.Height : transform.Height;

            _Effect.Projection = Matrix.CreateOrthographicOffCenter(0, width, height, 0, 1.0f, 1000.0f);
        }


        // TODO: Dispose _Effect
    }
}
